package com.sky.service.impl;

import com.sky.dto.GoodsSalesDTO;
import com.sky.entity.Orders;
import com.sky.mapper.OrdersMapper;
import com.sky.mapper.UserMapper;
import com.sky.service.ReportService;
import com.sky.service.UserService;
import com.sky.service.WorkspaceService;
import com.sky.vo.*;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ReportServiceImpl implements ReportService {
    @Autowired
    private OrdersMapper ordersMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private WorkspaceService workspaceService;

    /**
     * 营业额统计接口
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {
        //1.先计算日期
        List<LocalDate> dateArrayList = new ArrayList<>(); //日期列表
        dateArrayList.add(begin);

        while (!begin.isEqual(end)) {
            begin = begin.plusDays(1);//日期计算，获得指定日期后1天的日期
            dateArrayList.add(begin);
        }

        List<Double> turnoverArrayList = new ArrayList<>(); //营业额列表

        //2.再根据日期和已支付状态查询、计算金额

        //2.1遍历日期
        dateArrayList.forEach(date -> {
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
            Integer status = Orders.COMPLETED;
            Double turnover = ordersMapper.turnoverStatistics(beginTime, endTime, status);
            turnover = turnover == null ? 0.0 : turnover; //如果金额为空则设置为0.0
            turnoverArrayList.add(turnover);
        });

        //转化成String并以,分割
        String dateList = StringUtils.join(dateArrayList, ",");
        String turnoverList = StringUtils.join(turnoverArrayList, ",");

        TurnoverReportVO turnoverReportVO = new TurnoverReportVO();
        turnoverReportVO.setDateList(dateList);
        turnoverReportVO.setTurnoverList(turnoverList);
        return turnoverReportVO;
    }

    /**
     * 用户统计接口
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public UserReportVO userStatistics(LocalDate begin, LocalDate end) {
        //1.创建日期列表
        List<LocalDate> dateList = new ArrayList<>();
        dateList.add(begin);

        //日期计算，获得指定日期后1天的日期
        while (!begin.isEqual(end)) {
            begin = begin.plusDays(1);
            dateList.add(begin);
        }

        //2.创建新增用户列表
        List<Integer> newUserList = new ArrayList<>();

        //3.创建总用户量列表
        List<Integer> totalUserList = new ArrayList<>();

        //4.循环遍历日期
        for (LocalDate date : dateList) {
            //开始时间
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            //结束时间
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

            //创建map集合用以存放对象
            Map map = new HashMap<>();

            map.put("end", endTime);
            //先查询总人数
            Integer totalUser = userMapper.getUserStatistics(map);
            totalUserList.add(totalUser);

            map.put("begin", beginTime);
            //再查询新增人数
            Integer newUser = userMapper.getUserStatistics(map);
            newUserList.add(newUser);
        }


        return UserReportVO.builder().dateList(StringUtils.join(dateList, ",")).newUserList(StringUtils.join(newUserList, ",")).totalUserList(StringUtils.join(totalUserList, ",")).build();
    }

    /**
     * 订单统计接口
     * @param begin
     * @param end
     * @return
     */
    @Override
    public OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end) {
        //1.创建日期列表
        List<LocalDate> dateList = new ArrayList<>();
        dateList.add(begin);

        //日期计算，获得指定日期后1天的日期
        while (!begin.isEqual(end)) {
            begin = begin.plusDays(1);
            dateList.add(begin);
        }

        //2.构建订单数列表
        List<Integer> orderCountList = new ArrayList<>();
        //构建有效订单数列表列表
        List<Integer> validOrderCountList = new ArrayList<>();

        //3.订单完成率
        Double orderCompletionRate = 0.0;

        //4.循环遍历日期
        for (LocalDate date : dateList) {
            //开始时间
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            //结束时间
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

            //创建map集合用以存放对象
            Map map = new HashMap<>();
            map.put("begin", beginTime);
            map.put("end", endTime);

            //当天订单总数
            orderCountList.add(ordersMapper.CountOrdersStatistics(map));  //添加到订单列表集合

            map.put("status", Orders.COMPLETED);
            //当天有效订单数
            validOrderCountList.add(ordersMapper.CountOrdersStatistics(map)); //添加到有效订单列表集合
        }

        //5.计算时间区间内的订单总数量
        Integer totalOrderCount = orderCountList.stream().reduce(Integer::sum).get();

        //6.计算时间区间内的有效订单总数量
        Integer validOrderCount = validOrderCountList.stream().reduce(Integer::sum).get();

        //7.判断订单总量是否为零，并计算订单完成率
        if (totalOrderCount != 0){
            orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount;
        }

        //8.构建并返回OrderReportVO对象
        return OrderReportVO.builder()
                .dateList(StringUtils.join(dateList, ","))
                .orderCompletionRate(orderCompletionRate)
                .orderCountList(StringUtils.join(orderCountList,","))
                .totalOrderCount(totalOrderCount)
                .validOrderCount(validOrderCount)
                .validOrderCountList(StringUtils.join(validOrderCountList, ","))
                .build();
    }

    /**
     * 查询销量排名top10接口
     * @param begin
     * @param end
     * @return
     */
    @Override
    public SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end) {
        //1.构建需要参数
        //开始时间
        LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
        //结束时间
        LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);
        //订单状态设置为  5 - 已完成
        Integer status = Orders.COMPLETED;

        //2.动态查询商品销量列表
        List<GoodsSalesDTO> goodsSalesDTOList  = ordersMapper.getSalesTop10(beginTime,endTime,status);

        //3.构建商品名称列表
        List<String> names = goodsSalesDTOList.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());
        String nameList = StringUtils.join(names, ",");

        //4.构建销量列表
        List<Integer> numbers = goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());
        String numberList = StringUtils.join(numbers, ",");

        //5.返回数据
        return SalesTop10ReportVO.builder()
                .nameList(nameList)
                .numberList(numberList)
                .build();
    }

    /**
     * 导出近30天的运营数据报表
     * @param response
     */
    @Override
    public void exportBusinessData(HttpServletResponse response) {
        //1.构建参数
        //第一天日期
        LocalDate begin = LocalDate.now().minusDays(30);
        //第三十天日期
        LocalDate end = LocalDate.now().minusDays(1);
        //第一天开始时间
        LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
        //最后一天结束时间
        LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);

        //2.查询概览运营数据，提供给Excel模板文件
        BusinessDataVO businessDataVO = workspaceService.getBusinessData(beginTime, endTime);


        //3.构造读取文件的输入流
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");

        //4.写入数据
        try {
            //基于提供好的模板文件创建一个新的Excel表格对象
            XSSFWorkbook excel = new XSSFWorkbook(inputStream);
            XSSFSheet sheet = excel.getSheet("Sheet1");

            //运营报表日期
            sheet.getRow(1).getCell(1).setCellValue("日期："+begin + " 至 "+ end);

            //获得第4行
            XSSFRow row = sheet.getRow(3);
            row.getCell(2).setCellValue(businessDataVO.getTurnover()); //营业额
            row.getCell(4).setCellValue(businessDataVO.getOrderCompletionRate()); //订单完成率
            row.getCell(6).setCellValue(businessDataVO.getNewUsers()); //新增用户数
            //获得第5行
            row = sheet.getRow(4);
            row.getCell(2).setCellValue(businessDataVO.getValidOrderCount()); //有效订单数
            row.getCell(4).setCellValue(businessDataVO.getUnitPrice()); //平均客单价

            for (int i = 0;i < 30;i++){
                LocalDate date = begin.plusDays(i);
                //第一天开始时间
                beginTime = LocalDateTime.of(date, LocalTime.MIN);
                //第一天结束时间
                endTime = LocalDateTime.of(date, LocalTime.MAX);
                //准备明细数据
                BusinessDataVO businessData = workspaceService.getBusinessData(beginTime, endTime);

                XSSFRow sheetRow = sheet.getRow(7 + i);
                sheetRow.getCell(1).setCellValue(date.toString());
                sheetRow.getCell(2).setCellValue(businessData.getTurnover());
                sheetRow.getCell(3).setCellValue(businessData.getValidOrderCount());
                sheetRow.getCell(4).setCellValue(businessData.getOrderCompletionRate());
                sheetRow.getCell(5).setCellValue(businessData.getUnitPrice());
                sheetRow.getCell(6).setCellValue(businessData.getNewUsers());
            }
            //通过输出流将文件下载到客户端浏览器中
            ServletOutputStream outputStream = response.getOutputStream();
            excel.write(outputStream);

            //关闭资源
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            excel.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
